package com.xmlt.ui.editors.browsereditor;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.FileEditorInput;
import org.w3c.dom.Document;

import com.xmlt.core.template.Template;
import com.xmlt.core.template.TemplateException;
import com.xmlt.core.template.TemplateManager;
import com.xmlt.core.template.TemplateParameter;
import com.xmlt.core.template.contentgenerators.GeneratorRegistry;
import com.xmlt.core.template.types.ValidatorRegistry;
import com.xmlt.ui.builder.BuilderPlugin;
import com.xmlt.ui.editors.EditorsPlugin;

public class TemplateEditor extends BrowserEditor  {

	private String contents;
	private Template template;
	private TemplateManager manager;
	private String ref;
	private TemplateException exp;

	public TemplateEditor() {
	}
	
	@Override
	protected String getURL() {
		return EditorsPlugin.getResourceURL("site/template2.html");
	}
	
	@Override
	public Document getEditedDocument() {
		return template.getdocument();
	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
	throws PartInitException {
		super.init(site, input);
		
		IFile file = (IFile) input.getAdapter(IFile.class);
		manager = BuilderPlugin.getTemplateManager(file.getProject());
		ref = file.getProjectRelativePath().toString();
		try {
			template = manager.findTemplate(ref);
		} catch (TemplateException e) {
			exp = e;
		}
	}

	/**
	 * Saves the editor's document.
	 */
	public void doSave(IProgressMonitor monitor) {
		IEditorInput input = getEditorInput();
		if (input instanceof FileEditorInput) {
			IFile f = ((FileEditorInput) input).getFile();
			try {
				getBrowser().execute("getData()");
				f.setContents(new ByteArrayInputStream(contents.replaceAll("\u00a0"," ").getBytes()),
						IFile.REPLACE, monitor);
				f.refreshLocal(IResource.DEPTH_ONE, monitor);
				manager.findTemplate(ref, true);
				init(getEditorSite(), input);
			} catch (CoreException e) {
				exp = new TemplateException(e);
			} catch (TemplateException e) {
				exp = e;
			}
			setDirty(false);
			firePropertyChange(PROP_DIRTY);
		}
	}

	@Override
	protected void refresh() {
		try {
			manager.findTemplate(ref, true);
			getBrowser().execute("buildParamTree()");
		} catch (TemplateException e) {
			exp = e;
		}
	}

	@Override
	public String getContent() {
		IEditorInput input = getEditorInput();
		if (input instanceof FileEditorInput) {
			FileEditorInput fei = (FileEditorInput) input;
			IFile f = fei.getFile();
			try {
				f.refreshLocal(IFile.FORCE, null);
			} catch (CoreException e1) {
			}
			try {
				InputStream is = f.getContents();
				BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
				String contents = new String(new byte[]{}, "UTF-8");
				String s = null;
				while ((s = r.readLine()) != null) {
					contents += new String(s.getBytes(), "UTF-8");
				}
				return contents;
			} catch (CoreException e) {
				exp = new TemplateException(e);
			} catch (IOException e) {
				exp = new TemplateException(e);
			}
		}
		// TODO: add error reporting
		return "";
	}

	@Override
	public void setContent(String c) {
		contents = c;
	}

	@Override
	protected void addCustomfunctions() {
		new TreeContentGenerator();
		new ReferenceSelectGenerator();
		new GeneratorSelectGenerator();
	}
	
	private class GeneratorSelectGenerator extends MessengerFunction {

		public GeneratorSelectGenerator() {
			super("getGeneratorsString");
		}
		
		@Override
		public Object function(Object[] arguments) {
			String s = "<select onchange='setDirtyFlag(true);'>";
			s += "<option value=''>Select one</option>";
			List<String> ids = GeneratorRegistry.getAllGeneratorIds();
			for (String id : ids) {
				s += "<option value='"
						+ id + "' "
						+ (id.equals(template.getGeneratorId()) ? "selected"
								: "") + " >" + id + "</option>";
			}
			s += "</select>";
			return s;
		}
	}
	
	private class ReferenceSelectGenerator extends MessengerFunction {

		public ReferenceSelectGenerator() {
			super("getRefSelectString");
		}
		
		@Override
		public Object function(Object[] arguments) {
			String name = arguments[0].toString();
			String type = String.valueOf(arguments[1]);
			String s = "<select id='param_ref_select_" + name + "' onchange='setParamRef(\"" + name + "\", this);'>";
			s += "<option value=''>Select one</option>";
			if ("value".equals(type)) {
				List<String> validatorIds = ValidatorRegistry.getValidatorIds();
				for (String id : validatorIds) {
					s += "<option value='" + id + "'>" + id + "</option>";
				}
			} else {
				Map<String, Template> templates = manager.getAllTemplates();
				for (String ref : templates.keySet()) {
					Template t = templates.get(ref);
					if(t.equals(template))
						continue;
					
					s += "<option value='" + ref + "'>" + ref + "</option>";
				}
			}
			s += "</select>";
			return s;
		}
	}
	
	private class TreeContentGenerator extends MessengerFunction {

		public TreeContentGenerator() {
			super("getTreeContent");
		}
		
		@Override
		public Object function(Object[] arguments) {
			try {
				String str = "<ul id=\"editor_tree\" class=\"treeview-famfamfam\">";
				str += "<li><b>Special controls</b><ul>";
				str += "<li><span id='expression_draggable' class='expression draggable'><b>expression</b></span></li>";
				str += "<li><span id='condition_draggable' class='condition draggable'><b>condition</b></span></li>";
				str += "</ul></li>";
				str += "<li><b>Elements</b><ul>";
				if (template != null) {
					List<Template> usedTemplates = new ArrayList<Template>();
					usedTemplates.add(template);
					List<TemplateParameter> params = template.getParameters();
					for (TemplateParameter p: params) {
						try {
							str += getParamString(p, null, usedTemplates);
						} catch (TemplateException te) {
							continue;
						}
					}
				} else {
					str += "no linked template!";
				}
				str += "</ul></li></ul>";
				return str;
			} catch (Exception e) {
				return e.getMessage();
			}
		}
		
		private String getParamString(TemplateParameter p, String prefix, List<Template> usedTemplates) throws TemplateException {
			String str = "<li>";
			String id = prefix == null ? p.getName() : prefix + "."
					+ p.getName();
			if (p.isLink() || p.isReference()) {
				Template linkedTemplate = null;
				try {
					linkedTemplate = p.getTemplate();
				} catch (TemplateException te) {}
				if (linkedTemplate == null) {
					str += "<span><b>invalid reference</b></span>";
				} else {
					List<TemplateParameter> children = linkedTemplate.getParameters();
					str += "<span id='element_" + id
							+ "' class='element draggable' value='" + id + "'><b>"
							+ p.getName() + "</b></span>";
					if (p.isMultiple()) {
						str += "  <span id='loop_" + id
								+ "' class='loop draggable' value='" + id
								+ "'>loop</span>";
					}
					str += "<ul>";
					if (!usedTemplates.contains(linkedTemplate)) {
						List<Template> usedTemplatesCopy = new ArrayList<Template>();
						usedTemplatesCopy.addAll(usedTemplates);
						usedTemplatesCopy.add(linkedTemplate);
						for (TemplateParameter cp : children) {
							str += getParamString(cp, id, usedTemplatesCopy);
						}
					} else {
						str += "<span><b>Detected circular reference.<br/> Children cannot be loaded.</b></span>";
					}
					str += "</ul>";
				}
			} else if (p.isValue()) {
				str += "<span id='element_" + id
						+ "' class='element draggable' value='" + id + "'>"
						+ p.getName() + "</span>";
				if (p.isMultiple()) {
					str += "  <span id='loop_" + id
							+ "' class='loop draggable' value='" + id
							+ "'>loop</span>";
				}
			}
			str += "</li>";
			return str;
		}
		
	}
	
}
